﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using GeoAPI;
using GeoAPI.Geometries;
using GeoAPI.IO;
using NetTopologySuite.Geometries;
using RTools_NTS.Util;

namespace NetTopologySuite.IO
{
	public class WKTReader : ITextGeometryReader, IGeometryReader<string>, IGeometryIOSettings
	{
		public WKTReader() : this(GeometryServiceProvider.Instance.CreateGeometryFactory())
		{
		}

		public WKTReader(IGeometryFactory geometryFactory)
		{
			this._coordinateSequencefactory = geometryFactory.CoordinateSequenceFactory;
			this._precisionModel = geometryFactory.PrecisionModel;
			this.DefaultSRID = geometryFactory.SRID;
		}

		public IGeometryFactory Factory
		{
			get
			{
				return GeometryServiceProvider.Instance.CreateGeometryFactory(this._precisionModel, this.DefaultSRID, this._coordinateSequencefactory);
			}
			set
			{
				if (value != null)
				{
					this._coordinateSequencefactory = value.CoordinateSequenceFactory;
					this._precisionModel = value.PrecisionModel;
					this.DefaultSRID = value.SRID;
				}
			}
		}

		public int DefaultSRID { get; set; }

		public IGeometry Read(string wellKnownText)
		{
			IGeometry result;
			using (StringReader stringReader = new StringReader(wellKnownText))
			{
				result = this.Read(stringReader);
			}
			return result;
		}

		public IGeometry Read(Stream stream)
		{
			IGeometry result;
			using (StreamReader streamReader = new StreamReader(stream))
			{
				result = this.Read(streamReader);
			}
			return result;
		}

		public IGeometry Read(TextReader reader)
		{
			IGeometry result;
			try
			{
				IEnumerator<Token> enumerator = new StreamTokenizer(reader).GetEnumerator();
				enumerator.MoveNext();
				result = this.ReadGeometryTaggedText(enumerator);
			}
			catch (IOException ex)
			{
				throw new ParseException(ex.ToString());
			}
			return result;
		}

		internal IEnumerator<Token> Tokenizer(TextReader reader)
		{
			return new StreamTokenizer(reader).GetEnumerator();
		}

		internal IList<Token> Tokenize(TextReader reader)
		{
			StreamTokenizer streamTokenizer = new StreamTokenizer(reader);
			IList<Token> list = new List<Token>();
			streamTokenizer.Tokenize(list);
			return list;
		}

		private Coordinate[] GetCoordinates(IEnumerator<Token> tokens, bool skipExtraParenthesis, ref bool hasZ)
		{
			string text = WKTReader.GetNextEmptyOrOpener(tokens);
			if (text.Equals("EMPTY"))
			{
				return new Coordinate[0];
			}
			List<Coordinate> list = new List<Coordinate>();
			list.Add(this.GetPreciseCoordinate(tokens, skipExtraParenthesis, ref hasZ));
			text = WKTReader.GetNextCloserOrComma(tokens);
			while (text.Equals(","))
			{
				list.Add(this.GetPreciseCoordinate(tokens, skipExtraParenthesis, ref hasZ));
				text = WKTReader.GetNextCloserOrComma(tokens);
			}
			return list.ToArray();
		}

		private Coordinate GetPreciseCoordinate(IEnumerator<Token> tokens, bool skipExtraParenthesis, ref bool hasZ)
		{
			Coordinate coordinate = new Coordinate();
			bool flag = false;
			if (skipExtraParenthesis)
			{
				flag = WKTReader.IsStringValueNext(tokens, "(");
				if (flag)
				{
					tokens.MoveNext();
				}
			}
			coordinate.X = WKTReader.GetNextNumber(tokens);
			coordinate.Y = WKTReader.GetNextNumber(tokens);
			if (WKTReader.IsNumberNext(tokens))
			{
				coordinate.Z = WKTReader.GetNextNumber(tokens);
				if (!double.IsNaN(coordinate.Z))
				{
					hasZ = true;
				}
			}
			if (skipExtraParenthesis && flag && WKTReader.IsStringValueNext(tokens, ")"))
			{
				tokens.MoveNext();
			}
			this._precisionModel.MakePrecise(coordinate);
			return coordinate;
		}

		private static bool IsStringValueNext(IEnumerator<Token> tokens, string stringValue)
		{
			Token token = tokens.Current;
			if (token == null)
			{
				throw new InvalidOperationException("current Token is null");
			}
			return token.StringValue == stringValue;
		}

		private static bool IsNumberNext(IEnumerator<Token> tokens)
		{
			Token token = tokens.Current;
			return token is FloatToken || token is IntToken || (token is WordToken && string.Compare(token.Object.ToString(), WKTReader.NaNString, StringComparison.OrdinalIgnoreCase) == 0);
		}

		private static double GetNextNumber(IEnumerator<Token> tokens)
		{
			Token token = tokens.Current;
			if (!tokens.MoveNext())
			{
				throw new InvalidOperationException("premature end of enumerator");
			}
			if (token == null)
			{
				throw new System.ArgumentNullException("tokens", "Token list contains a null value");
			}
			if (token is EofToken)
			{
				throw new ParseException("Expected number but encountered end of stream");
			}
			if (token is EolToken)
			{
				throw new ParseException("Expected number but encountered end of line");
			}
			if (token is FloatToken || token is IntToken)
			{
				return (double)token.ConvertToType(typeof(double));
			}
			if (token is WordToken)
			{
				if (string.Compare(token.Object.ToString(), WKTReader.NaNString, StringComparison.OrdinalIgnoreCase) == 0)
				{
					return double.NaN;
				}
				throw new ParseException("Expected number but encountered word: " + token.StringValue);
			}
			else
			{
				if (token.StringValue == "(")
				{
					throw new ParseException("Expected number but encountered '('");
				}
				if (token.StringValue == ")")
				{
					throw new ParseException("Expected number but encountered ')'");
				}
				if (token.StringValue == ",")
				{
					throw new ParseException("Expected number but encountered ','");
				}
				throw new ParseException("Expected number but encountered '" + token.StringValue + "'");
			}
		}

		private static string GetNextEmptyOrOpener(IEnumerator<Token> tokens)
		{
			string nextWord = WKTReader.GetNextWord(tokens);
			if (nextWord.Equals("EMPTY") || nextWord.Equals("("))
			{
				return nextWord;
			}
			throw new ParseException("Expected 'EMPTY' or '(' but encountered '" + nextWord + "'");
		}

		private static string GetNextCloserOrComma(IEnumerator<Token> tokens)
		{
			string nextWord = WKTReader.GetNextWord(tokens);
			if (nextWord.Equals(",") || nextWord.Equals(")"))
			{
				return nextWord;
			}
			throw new ParseException("Expected ')' or ',' but encountered '" + nextWord + "'");
		}

		private static string GetNextCloser(IEnumerator<Token> tokens)
		{
			string nextWord = WKTReader.GetNextWord(tokens);
			if (nextWord.Equals(")"))
			{
				return nextWord;
			}
			throw new ParseException("Expected ')' but encountered '" + nextWord + "'");
		}

		private static string GetNextWord(IEnumerator<Token> tokens)
		{
			Token token = tokens.Current;
			if (token == null)
			{
				throw new InvalidOperationException("current token is null");
			}
			if (!tokens.MoveNext())
			{
				throw new InvalidOperationException("premature end of enumerator");
			}
			if (token is EofToken)
			{
				throw new ParseException("Expected number but encountered end of stream");
			}
			if (token is EolToken)
			{
				throw new ParseException("Expected number but encountered end of line");
			}
			if (token is FloatToken || token is IntToken)
			{
				throw new ParseException("Expected word but encountered number: " + token.StringValue);
			}
			if (token is WordToken)
			{
				return token.StringValue.ToUpper();
			}
			if (token.StringValue == "(")
			{
				return "(";
			}
			if (token.StringValue == ")")
			{
				return ")";
			}
			if (token.StringValue == ",")
			{
				return ",";
			}
			throw new InvalidOperationException("Should never reach here!");
		}

		internal IGeometry ReadGeometryTaggedText(IEnumerator<Token> tokens)
		{
			string nextWord = WKTReader.GetNextWord(tokens);
			int srid;
			if (nextWord == "SRID")
			{
				tokens.MoveNext();
				srid = Convert.ToInt32(WKTReader.GetNextNumber(tokens));
				tokens.MoveNext();
				nextWord = WKTReader.GetNextWord(tokens);
			}
			else
			{
				srid = this.DefaultSRID;
			}
			Token token = tokens.Current;
			if (token is WordToken)
			{
				if (token == "Z")
				{
					tokens.MoveNext();
				}
				else if (token == "ZM")
				{
					tokens.MoveNext();
				}
				else if (token == "M")
				{
					tokens.MoveNext();
				}
			}
			IGeometryFactory factory = GeometryServiceProvider.Instance.CreateGeometryFactory(this._precisionModel, srid, this._coordinateSequencefactory);
			IGeometry geometry;
			if (nextWord.Equals("POINT"))
			{
				geometry = this.ReadPointText(tokens, factory);
			}
			else if (nextWord.Equals("LINESTRING"))
			{
				geometry = this.ReadLineStringText(tokens, factory);
			}
			else if (nextWord.Equals("LINEARRING"))
			{
				geometry = this.ReadLinearRingText(tokens, factory);
			}
			else if (nextWord.Equals("POLYGON"))
			{
				geometry = this.ReadPolygonText(tokens, factory);
			}
			else if (nextWord.Equals("MULTIPOINT"))
			{
				geometry = this.ReadMultiPointText(tokens, factory);
			}
			else if (nextWord.Equals("MULTILINESTRING"))
			{
				geometry = this.ReadMultiLineStringText(tokens, factory);
			}
			else if (nextWord.Equals("MULTIPOLYGON"))
			{
				geometry = this.ReadMultiPolygonText(tokens, factory);
			}
			else
			{
				if (!nextWord.Equals("GEOMETRYCOLLECTION"))
				{
					throw new ParseException("Unknown type: " + nextWord);
				}
				geometry = this.ReadGeometryCollectionText(tokens, factory);
			}
			if (geometry == null)
			{
				throw new NullReferenceException("Error reading geometry");
			}
			return geometry;
		}

		private IPoint ReadPointText(IEnumerator<Token> tokens, IGeometryFactory factory)
		{
			if (WKTReader.GetNextEmptyOrOpener(tokens).Equals("EMPTY"))
			{
                return factory.CreatePoint((ICoordinateSequence)null);
			}
			bool hasZ = false;
			Coordinate preciseCoordinate = this.GetPreciseCoordinate(tokens, false, ref hasZ);
			IPoint result = factory.CreatePoint(this.ToSequence(hasZ, new Coordinate[]
			{
				preciseCoordinate
			}));
			WKTReader.GetNextCloser(tokens);
			return result;
		}

		private ICoordinateSequence ToSequence(bool hasZ, params Coordinate[] coords)
		{
			int num = hasZ ? 3 : 2;
			ICoordinateSequence coordinateSequence = this._coordinateSequencefactory.Create(coords.Length, num);
			for (int i = 0; i < coords.Length; i++)
			{
				coordinateSequence.SetOrdinate(i, Ordinate.X, coords[i].X);
				coordinateSequence.SetOrdinate(i, Ordinate.Y, coords[i].Y);
			}
			if (num == 3)
			{
				for (int j = 0; j < coords.Length; j++)
				{
					coordinateSequence.SetOrdinate(j, Ordinate.Z, coords[j].Z);
				}
			}
			return coordinateSequence;
		}

		private ILineString ReadLineStringText(IEnumerator<Token> tokens, IGeometryFactory factory)
		{
			bool hasZ = false;
			Coordinate[] coordinates = this.GetCoordinates(tokens, false, ref hasZ);
			return factory.CreateLineString(this.ToSequence(hasZ, coordinates));
		}

		private ILinearRing ReadLinearRingText(IEnumerator<Token> tokens, IGeometryFactory factory)
		{
			bool hasZ = false;
			Coordinate[] coordinates = this.GetCoordinates(tokens, false, ref hasZ);
			return factory.CreateLinearRing(this.ToSequence(hasZ, coordinates));
		}

		private IMultiPoint ReadMultiPointText(IEnumerator<Token> tokens, IGeometryFactory factory)
		{
			bool hasZ = false;
			Coordinate[] coordinates = this.GetCoordinates(tokens, true, ref hasZ);
			return factory.CreateMultiPoint(this.ToPoints(this.ToSequence(hasZ, coordinates), factory));
		}

		private IPoint[] ToPoints(ICoordinateSequence coordinates, IGeometryFactory factory)
		{
			IPoint[] array = new IPoint[coordinates.Count];
			for (int i = 0; i < coordinates.Count; i++)
			{
				ICoordinateSequence coordinateSequence = this._coordinateSequencefactory.Create(1, coordinates.Ordinates);
				CoordinateSequences.Copy(coordinates, i, coordinateSequence, 0, 1);
				array[i] = factory.CreatePoint(coordinateSequence);
			}
			return array;
		}

		private IPolygon ReadPolygonText(IEnumerator<Token> tokens, IGeometryFactory factory)
		{
			string text = WKTReader.GetNextEmptyOrOpener(tokens);
			if (text.Equals("EMPTY"))
			{
				return factory.CreatePolygon(factory.CreateLinearRing(new Coordinate[0]), new ILinearRing[0]);
			}
			List<ILinearRing> list = new List<ILinearRing>();
			ILinearRing shell = this.ReadLinearRingText(tokens, factory);
			text = WKTReader.GetNextCloserOrComma(tokens);
			while (text.Equals(","))
			{
				ILinearRing item = this.ReadLinearRingText(tokens, factory);
				list.Add(item);
				text = WKTReader.GetNextCloserOrComma(tokens);
			}
			return factory.CreatePolygon(shell, list.ToArray());
		}

		private IMultiLineString ReadMultiLineStringText(IEnumerator<Token> tokens, IGeometryFactory factory)
		{
			string text = WKTReader.GetNextEmptyOrOpener(tokens);
			if (text.Equals("EMPTY"))
			{
				return factory.CreateMultiLineString(new ILineString[0]);
			}
			List<ILineString> list = new List<ILineString>();
			ILineString item = this.ReadLineStringText(tokens, factory);
			list.Add(item);
			text = WKTReader.GetNextCloserOrComma(tokens);
			while (text.Equals(","))
			{
				item = this.ReadLineStringText(tokens, factory);
				list.Add(item);
				text = WKTReader.GetNextCloserOrComma(tokens);
			}
			return factory.CreateMultiLineString(list.ToArray());
		}

		private IMultiPolygon ReadMultiPolygonText(IEnumerator<Token> tokens, IGeometryFactory factory)
		{
			string text = WKTReader.GetNextEmptyOrOpener(tokens);
			if (text.Equals("EMPTY"))
			{
				return factory.CreateMultiPolygon(new IPolygon[0]);
			}
			List<IPolygon> list = new List<IPolygon>();
			IPolygon item = this.ReadPolygonText(tokens, factory);
			list.Add(item);
			text = WKTReader.GetNextCloserOrComma(tokens);
			while (text.Equals(","))
			{
				item = this.ReadPolygonText(tokens, factory);
				list.Add(item);
				text = WKTReader.GetNextCloserOrComma(tokens);
			}
			return factory.CreateMultiPolygon(list.ToArray());
		}

		private IGeometryCollection ReadGeometryCollectionText(IEnumerator<Token> tokens, IGeometryFactory factory)
		{
			string text = WKTReader.GetNextEmptyOrOpener(tokens);
			if (text.Equals("EMPTY"))
			{
				return factory.CreateGeometryCollection(new IGeometry[0]);
			}
			List<IGeometry> list = new List<IGeometry>();
			IGeometry item = this.ReadGeometryTaggedText(tokens);
			list.Add(item);
			text = WKTReader.GetNextCloserOrComma(tokens);
			while (text.Equals(","))
			{
				item = this.ReadGeometryTaggedText(tokens);
				list.Add(item);
				text = WKTReader.GetNextCloserOrComma(tokens);
			}
			return factory.CreateGeometryCollection(list.ToArray());
		}

		public bool HandleSRID
		{
			get
			{
				return true;
			}
			set
			{
			}
		}

		public Ordinates AllowedOrdinates
		{
			get
			{
				return Ordinates.XYZ;
			}
		}

		public Ordinates HandleOrdinates
		{
			get
			{
				return this.AllowedOrdinates;
			}
			set
			{
			}
		}

		public bool RepairRings { get; set; }

		private ICoordinateSequenceFactory _coordinateSequencefactory;

		private IPrecisionModel _precisionModel;

		private static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture;

		private static readonly string NaNString = double.NaN.ToString(WKTReader.InvariantCulture);
	}
}
